package com.sl.ms.carriage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.CarriageDTO;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.enums.CarriageExceptionEnum;
import com.sl.ms.carriage.handler.CarriageChainHandlerFactory;
import com.sl.ms.carriage.mapper.CarriageMapper;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.ms.carriage.utils.CarriageUtils;
import com.sl.transport.common.exception.SLException;
import io.swagger.models.auth.In;
import org.bouncycastle.jcajce.provider.asymmetric.X509;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Wrapper;
import java.util.*;
import java.util.stream.Collectors;

/**
 * description:
 *
 * @Author youhongwei
 * @Create 2025/6/20 18:14
 * @Version 1.0
 */
@Service
public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService {

    @Resource
    private AreaFeign areaFeign;

    @Resource
    private CarriageChainHandlerFactory carriageChainHandlerFactory;

//    @Resource
//    private FindEco

    @Override
    public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) {

        // 非经济区 和 经济区
        Integer templateType = carriageDto.getTemplateType();

        Long id = carriageDto.getId();

        // 判断是不是经济区
        if(templateType != CarriageConstant.ECONOMIC_ZONE){
            //查询是否已存在相同类型的模版
            CarriageEntity carriageEntity = this.findByTemplateType(carriageDto.getTemplateType());

            //判断ID是不是null 如果是null就进行插入操作 否则就进行修改操作
            if(id == null){
                //判断carriageEntity是否为null 如果为null就进行保存否则就抛出异常
                if(carriageEntity == null){
                    //进行保存
                    this.saveOrUpdate(CarriageUtils.toEntity(carriageDto));
                }else {
                    // 抛出异常
                    throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
                }
            }else{
                //判断carriageEntity是否为null 如果不为null就进行修改否则就抛出异常
                if(carriageEntity == null){
                    this.saveOrUpdate(CarriageUtils.toEntity(carriageDto));
                }else{
                    throw new SLException(CarriageExceptionEnum.NOT_FOUND);
                }
            }
            return carriageDto;
        }

        //经济区
        //先查询所有的经济区模版
        List<CarriageEntity> economicList = this.findByEconomicTemplateType();
        //存储所有的经济区包含城市
        List<String> eoncoset = null;
        //遍历所有经济区模版
        economicList.forEach(carriageEntity->{
            eoncoset.add(carriageEntity.getAssociatedCity());
        });
        // 检查当前末班的城市是否与经济区模版的城市有相同的
        Collection<String> search = CollUtil.intersection(eoncoset, carriageDto.getAssociatedCityList());
        //如果有则抛出异常
        if(CollUtil.isEmpty(search)){
            throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
        }
        //没有就保存模版
//        this.saveOrUpdate(CarriageUtils.toEntity(carriageDto));

        this.saveOrUpdate(CarriageUtils.toEntity(carriageDto));

       return carriageDto;
    }

    @Override
    public List<CarriageDTO> findAll() {
        LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery().orderByDesc(CarriageEntity::getCreated);
        List<CarriageEntity> list = this.list(wrapper);

//        List<CarriageDTO> returnList = new ArrayList<>();
//        list.forEach(carriageEntity->{
//            returnList.add(CarriageUtils.toDTO(carriageEntity));
//        });
//        return returnList;

        //流
        return list.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());


    }

    @Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {


        //根据参数查询运费模版
//        CarriageEntity carriage = this.findCarriage(waybillDTO);

        CarriageEntity carriage = carriageChainHandlerFactory.findCarriageEntity(waybillDTO);


        //链式模式实现
        //就是把上面多个分支，封装一个个对象
        //在入口的地方调用统一的处理方法

        //计算重量，最小重量为1kg
        double computeWeight = this.getComputeWeight(waybillDTO, carriage);
        //计算运费 首重+续重
        double expense = carriage.getFirstWeight() + ((computeWeight - 1) * carriage.getContinuousWeight());
        //保留一位小数
        expense = NumberUtil.round(expense,1).doubleValue();
        //封装运费和计算重量到DTO,并返回
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage);
        carriageDTO.setComputeWeight(computeWeight);
        carriageDTO.setExpense(expense);
        return carriageDTO;

    }

    private double getComputeWeight(WaybillDTO waybillDTO,CarriageEntity carriage){
        //计算体积，如果传入的是体积需要计算
//        Integer volume = waybillDTO.getVolume();
        Integer volume = waybillDTO.getVolume();
        if(ObjectUtil.isNotEmpty(volume)){
            try {
                //长*宽*高 计算体积
                volume = waybillDTO.getMeasureLong() * waybillDTO.getMeasureWidth() * waybillDTO.getMeasureHigh();
            } catch (Exception e) {

                volume = 0;
            }
        }
        //计算体积重量 体积 \ 轻抛系数
        BigDecimal volumeWeight = NumberUtil.div(volume, carriage.getLightThrowingCoefficient(), 1);
        //取最大值
        double computeWeight = NumberUtil.max(volumeWeight.doubleValue(),NumberUtil.round(waybillDTO.getWeight(),1).doubleValue());
        //计算续重
        //判断如果重量小于1kg就返回1kg

        if(computeWeight<=1){
            return 1;
        }
        //判断如果重量小于等于10kg就但会本身
        if(computeWeight <= 10){
            return computeWeight;
        }
        //举例
        //108.4kg按照108kg
        //108.5kg按照109kg
        //108。6kg按照109kg
        //判断如果重量大于等于100kg就四舍五入取整
        if(computeWeight >= 100){
            return NumberUtil.round(computeWeight,0).doubleValue();
        }

        //0.5kg为一个计算单位
        //18.8kg按照19kg收费
        //18.4kg按照18.5kg收费
        //18.1kg按照18.5kg收费
        //18.6kg按照19kg收费
        // 将重量向下取整（即舍弃小数部分）并转换为整数
        //将重量在10kg到100kg之间的重量进行取整
        int integer = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).intValue();

        //判断实际的重量与整数的重量 相减是否为0 如果为0就返回整数
        if(NumberUtil.sub(computeWeight,integer) == 0){
             return integer;
        }
        //判断实际的重量与整数的重量 相减是否小于等于0.5kg 如果小于等于0.5kg 就让整数加上0.5kg
        if(NumberUtil.sub(computeWeight,integer) <= 0.5){
            return NumberUtil.add(integer,0.5);
        }
        //否则就大于0.5kg 就让整数加上1kg
        return NumberUtil.add(integer,1);
    }


    /**
     * 查找运费模版
     * @param waybillDTO
     * @return
     */
    private CarriageEntity findCarriage(WaybillDTO waybillDTO){
        //判断城市id是不是相同的

        if(ObjectUtil.equal(waybillDTO.getReceiverCityId(),waybillDTO.getSenderCityId())){
            CarriageEntity carriage = this.findByTemplateType(CarriageConstant.SAME_CITY);
            if(ObjectUtil.isNotEmpty(carriage)){
                return carriage;
            }
        }

        // 获取收寄件地址省份的id
        Long receiverParentId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderParentId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
        //判断省份id是不是相同的
        if(ObjectUtil.equal(receiverParentId,senderParentId)){
            CarriageEntity carriage = this.findByTemplateType(CarriageConstant.SAME_PROVINCE);
            if(ObjectUtil.isNotEmpty(carriage)){
                return carriage;
            }
        }

        //查询经济区互寄
        CarriageEntity carriage = this.findEconomicCarriage(receiverParentId, senderParentId);
        if(ObjectUtil.isNotEmpty(carriage)){
            return carriage;
        }

        //跨省

        carriage = this.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
        if(ObjectUtil.isNotEmpty(carriage)){
            return carriage;
        }
        throw new SLException(CarriageExceptionEnum.NOT_FOUND);
    }


    /**
     *  查找经济区运费模版
     * @param receiverParentId
     * @param senderParentId
     * @return
     */
    public CarriageEntity findEconomicCarriage(Long receiverParentId,Long senderParentId){
        //获取所有的经济区枚举
        LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        //查找包含两个省份的经济区
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : enumMap.values()) {
            //该经济区是否全部包含收发省id
            boolean b = ArrayUtil.containsAll(regionEnum.getValue(), receiverParentId, senderParentId);
            if(b){
                economicRegionEnum = regionEnum;
                break;
            }
        }
        if(economicRegionEnum == null){
            return null;
        }
        //根据类型编码查询
        LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.lambdaQuery(CarriageEntity.class)
                .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
                .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
                .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());
        return super.getOne(wrapper);

    }


    @Override
    public CarriageEntity findByTemplateType(Integer templateType) {

        LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery().eq(CarriageEntity::getTemplateType, templateType);
        CarriageEntity carriageEntity = this.getOne(wrapper);

        return carriageEntity;
    }


    @Override
    public List<CarriageEntity> findByEconomicTemplateType() {

        LambdaQueryWrapper<CarriageEntity> wrapper = Wrappers.<CarriageEntity>lambdaQuery()
                .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE);
        List<CarriageEntity> list = this.list(wrapper);

        return list;
    }
}
